package han.cirno.corrupt.modules.classes

import androidx.activity.compose.LocalActivity
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import han.cirno.corrupt.compose.localeStringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import han.cirno.corrupt.R
import han.cirno.corrupt.activity.main.LocalBottomPadding
import han.cirno.corrupt.activity.main.MainActivityViewModel
import han.cirno.corrupt.components.calendar.CalendarUtil
import han.cirno.corrupt.components.school.SchoolImpl
import han.cirno.corrupt.components.school.SchoolProvider
import han.cirno.corrupt.compose.QuickCard
import han.cirno.corrupt.compose.VerticalSpacer
import han.cirno.corrupt.data.instances.UserClassesData
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.util.Calendar
import java.util.Locale

val dayOfWeekText= arrayOf(
    R.string.dow_mon,
    R.string.dow_tue,
    R.string.dow_wed,
    R.string.dow_thur,
    R.string.dow_fri,
    R.string.dow_sat,
    R.string.dow_sun,
)

@Composable
fun ClassesLayout(){
    val scope = rememberCoroutineScope()
    var currentTime by remember { mutableLongStateOf(System.currentTimeMillis()) }
    DisposableEffect(Unit) {
        val job = scope.launch {
            while (isActive) {
                currentTime = System.currentTimeMillis()
                delay(1000L)
            }
        }
        onDispose {
            job.cancel()
        }
    }
    val provider = SchoolImpl.impl
    val mainActivityViewModel:MainActivityViewModel= hiltViewModel(LocalActivity.current as AppCompatActivity)
    val classTable = remember { UserClassesData.tryFetchClassTable() }
    val classes=classTable?.classEntries
    val initPage=if (provider!=null){
        CalendarUtil.calcCurrentWeeks(
            UserClassesData.tryFetchFirstDay(),
            Calendar.getInstance()
        )
    }else 0
    val pageCount = rememberPagerState(initPage) { if(provider==null|| classes?.isNotEmpty()!=true)1 else 20 }
    val timeIndicator by mainActivityViewModel.configTimeLineInspectorState.collectAsState()
    val timeIndicatorAlpha by mainActivityViewModel.configTimeLineInspectorAlphaState.collectAsState()
    val dayIndicator by mainActivityViewModel.configDayInspectorState.collectAsState()
    val dayIndicatorAlpha by mainActivityViewModel.configDayInspectorAlphaState.collectAsState()

    HorizontalPager(
        modifier = Modifier.fillMaxSize(),
        state = pageCount) {page-> Column{
        if(provider==null|| classes?.isNotEmpty()!=true){
            InvalidCard()
            VerticalSpacer(LocalBottomPadding.current)
            return@HorizontalPager
        }
        val firstDay=UserClassesData.tryFetchFirstDay()
        val nowCalendar=Calendar.getInstance()
        val calendar= Calendar.getInstance()
        calendar.timeInMillis= firstDay
        calendar.add(Calendar.DATE,page*7)
        val validClasses=classes.filter { it.weeks.contains(page+1) }
        Box(modifier = Modifier.weight(1f)) {
            var isInCurrentWeek=false
            Row(horizontalArrangement = Arrangement.Start) {
                TimeIndicator(page, provider)
                for (i in 0 until 7) {
                    val isInCurrentDay=
                        calendar[Calendar.YEAR] == nowCalendar[Calendar.YEAR] &&
                                calendar[Calendar.MONTH] == nowCalendar[Calendar.MONTH] &&
                                calendar[Calendar.DAY_OF_MONTH] == nowCalendar[Calendar.DAY_OF_MONTH]
                    if (isInCurrentDay)isInCurrentWeek=true
                    ClassesColumn(
                        if (isInCurrentDay&& dayIndicator) dayIndicatorAlpha / 1000f
                        else null,
                        calendar[Calendar.MONTH] + 1,
                        calendar[Calendar.DAY_OF_MONTH],
                        i,
                        validClasses,
                        provider
                    )
                    calendar.add(Calendar.DATE, 1)
                }
            }
            if(timeIndicator&&isInCurrentWeek){
                @Suppress("UNUSED_EXPRESSION") currentTime
                val density = LocalDensity.current
                var sizeInDp by remember { mutableStateOf(DpSize.Zero) }
                val timeWeight= getTimeWeight(provider)
                val nowTimeString=
                    localeStringResource(R.string.classes_page_timeIndicator,
                        nowCalendar[Calendar.HOUR_OF_DAY],
                        nowCalendar[Calendar.MINUTE],
                        nowCalendar[Calendar.SECOND])
                val color= MaterialTheme.colorScheme.primary.copy(alpha = timeIndicatorAlpha/1000f)
                Column(modifier = Modifier.fillMaxSize()) {
                    Spacer(modifier = Modifier
                        .weight(0.05f)
                        .fillMaxWidth())
                    Column(modifier = Modifier.weight(1f).onSizeChanged { newSize ->
                        sizeInDp = with(density) {
                            DpSize(newSize.width.toDp(), newSize.height.toDp())
                        }
                    }) {
                        Spacer(
                            modifier = Modifier
                                .fillMaxWidth()
                                .height(sizeInDp.height*timeWeight/provider.getClassTime().getSerialLength())
                        )
                        Column(modifier = Modifier.fillMaxWidth()) {
                            Box(modifier = Modifier.fillMaxWidth().height(2.dp)
                                .background(color))
                            Row(modifier = Modifier.fillMaxWidth()) {
                                Spacer(modifier = Modifier.weight(0.7f))
                                Text(modifier = Modifier.weight(7f), text = nowTimeString,
                                    style = MaterialTheme.typography.bodyMedium.copy(color = color),
                                    textAlign = when (nowCalendar[Calendar.DAY_OF_WEEK]){
                                        Calendar.MONDAY,Calendar.TUESDAY,Calendar.WEDNESDAY,Calendar.THURSDAY->TextAlign.Right
                                        else->TextAlign.Left
                                    })
                            }
                        }
                    }
                }
            }
        }
        VerticalSpacer(LocalBottomPadding.current)
    }}
}

@Composable
fun InvalidCard()= Column(
    modifier = Modifier.padding(64.dp)) {
    QuickCard {
        Text(
            modifier = Modifier.fillMaxWidth(),
            textAlign = TextAlign.Center,
            style = MaterialTheme.typography.titleLarge,
            text = localeStringResource(R.string.classes_page_invalid_title))
        Text(
            modifier = Modifier.fillMaxWidth(),
            textAlign = TextAlign.Center,
            text = localeStringResource(R.string.classes_page_invalid_subtitle))
        VerticalSpacer(4.dp)
        Text(
            modifier = Modifier.fillMaxWidth(),
            textAlign = TextAlign.Center,
            text = localeStringResource(R.string.classes_page_invalid_text))
    }
}

@Composable
fun RowScope.ClassesColumn(
    background:Float?,
    month: Int,
    day: Int,
    dayOfWeek: Int,
    validClasses: List<UserClassesData.ClassEntry>,
    provider: SchoolProvider
)= Column (
    modifier = Modifier.fillMaxWidth().weight(1f)
        .then(
            if (background==null)Modifier
            else Modifier.background(
                color=MaterialTheme.colorScheme.primary.copy(alpha = background),
                shape = RoundedCornerShape(4.dp)))
        ){
    val dayString= (if (day == 1) "$month." else "")+day
    val dayClasses=
        validClasses
            .filter { it.dow== UserClassesData.DayOfWeek.valueOf(dayOfWeek) }
            .sortedBy { it.time.from }
    Column (
        modifier = Modifier.weight(0.05f),
        verticalArrangement = Arrangement.Center) {
        Text(
            modifier = Modifier.fillMaxWidth(),
            textAlign = TextAlign.Center,
            style = MaterialTheme.typography.bodySmall,
            text = localeStringResource(dayOfWeekText[dayOfWeek])
        )
        Text(
            modifier = Modifier.fillMaxWidth(),
            textAlign = TextAlign.Center,
            style = MaterialTheme.typography.bodySmall,
            text = dayString
        )
    }
    var lastTime=0
    Column(modifier = Modifier.weight(1f)) {
        dayClasses.forEach {
            val blankSize=it.time.from-lastTime
            if(blankSize!=0) Spacer(modifier = Modifier.weight(blankSize.toFloat()))
            Card(modifier = Modifier
                .weight(it.time.to-it.time.from.toFloat()+1)
                .fillMaxSize()
                .padding(2.dp),
                elevation = CardDefaults.cardElevation(defaultElevation = 2.dp),
                shape = RoundedCornerShape(4.dp),
                onClick = {}) {
                Column(modifier = Modifier.fillMaxSize(),
                    verticalArrangement = Arrangement.Center) {
                    Text(
                        modifier = Modifier.fillMaxWidth(),
                        textAlign = TextAlign.Center,
                        style = MaterialTheme.typography.bodySmall,
                        text = it.name)
                    Text(
                        modifier = Modifier.fillMaxWidth(),
                        textAlign = TextAlign.Center,
                        style = MaterialTheme.typography.bodySmall,
                        text = it.place)
                }
            }
            lastTime=it.time.to+1
        }
        if(lastTime<provider.getClassTime().getSerialLength())
            Spacer(modifier = Modifier.weight(provider.getClassTime().getSerialLength()-lastTime.toFloat()))
    }
}

@Composable
fun RowScope.TimeIndicator(page:Int, provider: SchoolProvider)=Column(
    modifier = Modifier.fillMaxHeight().weight(0.7f),
    verticalArrangement = Arrangement.Center,
    horizontalAlignment = Alignment.CenterHorizontally){
    val classTime = provider.getClassTime()
    Box(
        modifier = Modifier.weight(0.05f).fillMaxWidth(),
        contentAlignment = Alignment.Center) {
        Text(
            textAlign = TextAlign.Center,
            style = MaterialTheme.typography.bodySmall,
            text = localeStringResource(R.string.classes_page_week, page + 1)
        )
    }
    Column(modifier = Modifier.weight(1f).fillMaxWidth()) {
        for (i in 0 until classTime.getSerialLength()) {
            val (fromSecond, _) = classTime.getTime(i)
            val (hour, second) = CalendarUtil.getHourAndMinute(fromSecond)
            Column(
                modifier = Modifier.weight(1f).fillMaxWidth(),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally) {
                Text(
                    text = "${i + 1}",
                    textAlign = TextAlign.Center,)
                Text(
                    textAlign = TextAlign.Center,
                    style = MaterialTheme.typography.bodySmall,
                    text = String.format(Locale.getDefault(), "%02d:%02d", hour, second))
            }
        }
    }
}

fun getTimeWeight(provider: SchoolProvider):Float{
    val classTime = provider.getClassTime()
    val calendar=Calendar.getInstance()
    val hour=calendar[Calendar.HOUR_OF_DAY]
    val minute=calendar[Calendar.MINUTE]
    val second=calendar[Calendar.SECOND]
    val absSecond=hour*60*60+minute*60+second
    return run {
        val serialLength = classTime.getSerialLength()
        for (i in 0 until serialLength) {
            val time=classTime.getTime(i)
            if(time.first<=absSecond&&time.second<=absSecond)continue
            if (time.first>=absSecond)return@run i.toFloat()
            if (time.second>=absSecond){
                val k=(absSecond-time.first)/(time.second-time.first).toFloat()
                return@run (i+k)
            }
            return@run (i+1f)
        }
        serialLength.toFloat()
    }
}